package com.ilink.groundservice.service.impl.InnovationPlaza;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ilink.groundservice.entity.PO.innovationPlaza.AnnouncementInfo;
import com.ilink.groundservice.entity.PO.innovationPlaza.CompetitionMetadata;
import com.ilink.groundservice.entity.PO.innovationPlaza.InnovationConnectTags;
import com.ilink.groundservice.entity.PO.innovationPlaza.InnovationTags;
import com.ilink.groundservice.entity.VO.innovationPlaza.CompetitionResponse;
import com.ilink.groundservice.mapper.innovationPlaza.AnnouncementInfoMapper;
import com.ilink.groundservice.mapper.innovationPlaza.CompetitionMapper;
import com.ilink.groundservice.mapper.innovationPlaza.InnovationConnectTagsMapper;
import com.ilink.groundservice.mapper.innovationPlaza.InnovationTagsMapper;
import com.ilink.groundservice.mapper.innovationPlaza.UserLikeMapper;
import com.ilink.groundservice.mapper.langflowApp.TagMapper;
import com.ilink.groundservice.service.InnovationPlaza.CompetitionService;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@CacheConfig(cacheNames = "competition")
public class CompetitionServiceImpl implements CompetitionService {

    @Autowired
    private CompetitionMapper competitionMapper;
    @Autowired
    private UserLikeMapper userLikeMapper;

    @Autowired
    private InnovationTagsMapper innovationTagsMapper;

    @Autowired
    private InnovationConnectTagsMapper innovationConnectTagsMapper;

    @Autowired
    private AnnouncementInfoMapper announcementInfoMapper;
    @Autowired
    private TagMapper tagMapper;


    @Override
    public List<InnovationTags> getCommonTags() {
        return innovationTagsMapper.selectCommonCompetitionTags();
    }

    @Override
    public IPage<CompetitionResponse> getAllCompetitions(String sentence, List<String> tags,
        String sortBy, int pageNum, int pageSize, String userId) {
        Page<CompetitionResponse> page = new Page<>(pageNum, pageSize);

        IPage<CompetitionResponse> competitionResponsePage = competitionMapper.getAllCompetitions(
            page, sortBy);

        List<CompetitionResponse> competitionResponses = competitionResponsePage.getRecords();

        // 获取每个赛事的标签并设置
        for (CompetitionResponse competitionResponse : competitionResponses) {
            List<String> projectTags = innovationTagsMapper.getTagsByProjectId(
                competitionResponse.getCompetitionId());
            competitionResponse.setTags(projectTags);
            //获取收藏状态
            int count = userLikeMapper.countUserCollect(competitionResponse.getCompetitionId(),
                userId);
            competitionResponse.setLikeTrue(count > 0);

        }

        if (tags.contains("全部")) {
            // 将 tags 修改为空列表
            tags.clear();
        }
        // 按需过滤项目
        List<CompetitionResponse> filteredCompetitions = competitionResponses;
        boolean filterByTags = tags != null && !tags.isEmpty();
        boolean filterBySentence = sentence != null && !sentence.isEmpty();

        if (filterByTags && filterBySentence) {
            // 同时过滤标签和模糊查询
            filteredCompetitions = competitionResponses.stream()
                .filter(
                    competition -> competition.getTags() != null && !competition.getTags().isEmpty()
                        && new HashSet<>(competition.getTags()).containsAll(tags))
                .filter(competition -> competition.getName() != null && competition.getName()
                    .contains(sentence))
                .collect(Collectors.toList());
        } else if (filterByTags) {
            // 根据标签进行过滤
            filteredCompetitions = filteredCompetitions.stream()
                .filter(
                    competition -> competition.getTags() != null && !competition.getTags().isEmpty()
                        && new HashSet<>(competition.getTags()).containsAll(tags))
                .collect(Collectors.toList());
        } else if (filterBySentence) {
            // 进行模糊查询过滤
            filteredCompetitions = filteredCompetitions.stream()
                .filter(competition -> competition.getName() != null && competition.getName()
                    .contains(sentence))
                .collect(Collectors.toList());
        }

        // 设置过滤后的数据到分页对象中
        page.setRecords(filteredCompetitions);
        page.setTotal(competitionResponsePage.getTotal());
        page.setPages(competitionResponsePage.getPages());
//        page.setSize(competitionResponsePage.getSize());
        page.setCurrent(competitionResponsePage.getCurrent());
        page.setTotal(filteredCompetitions.size());

        return page;

    }

    /**
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.ilink.groundservice.entity.VO.innovationPlaza.CompetitionResponse>
     * @Author Black Curry
     * @Description 条件分页查询竞赛信息；采用SpringCache和redis
     * 为mysql进行数据缓存。当数据库更新时需要在指定位置清理该缓存，直接清理该类下的所有缓存，保证数据弱一致性。
     * @Date 18:31 2024/9/29
     * @Param [sentence, tags, sortBy, pageNum, pageSize]
     **/
    @Override
    @Cacheable(key = "#sentence + '-' + #tags + '-' + #sortBy + '-' + #pageNum + '-' + #pageSize", unless = "#result == null")
    public IPage<CompetitionResponse> getAllCompetitionsEnd(String sentence, List<String> tags,
        String sortBy, int pageNum, int pageSize) {
        //按照关键词、标签、排序等条件进行筛选，返回分页数据
        LambdaQueryWrapper<CompetitionMetadata> queryWrapper = new LambdaQueryWrapper<>();

        //1.按照关键词模糊过滤所有的数据
        if (sentence != null && !sentence.isEmpty()) {
            //模糊查询获取更多匹配的标签,当全部时不考虑标签过滤影响
            queryWrapper.like(CompetitionMetadata::getName, sentence)
                .or()
                .like(CompetitionMetadata::getDescription, sentence);
        } else {
            sentence = "";
        }
        //2.总的标签查询
        if (tags != null && tags.contains("全部")) {
            tags.clear();  // 将 tags 修改为空列表
        }

        if (tags != null && !tags.isEmpty()) {
            List<Long> EndTagIds = new ArrayList<>();
            if (!sentence.isEmpty()) {
                EndTagIds = innovationTagsMapper.selectList(new LambdaQueryWrapper<InnovationTags>()
                    .in(InnovationTags::getName, tags)
                    .or()
                    .like(InnovationTags::getName, sentence)
                ).stream().map(InnovationTags::getTagId).collect(Collectors.toList());
            } else {
                EndTagIds = innovationTagsMapper.selectList(
                    new LambdaQueryWrapper<InnovationTags>().in(InnovationTags::getName, tags)
                ).stream().map(InnovationTags::getTagId).collect(Collectors.toList());
            }

            queryWrapper.in(CompetitionMetadata::getCompetitionId,
                innovationConnectTagsMapper.selectList(
                        new LambdaQueryWrapper<InnovationConnectTags>()
                            .in(InnovationConnectTags::getTagId, EndTagIds)).stream()
                    .map(InnovationConnectTags::getProjectId).collect(
                        Collectors.toList()));

        }
        //3.增加排序
        // 定义可排序的字段列表
        List<String> validSortFields = Arrays.asList("publisher_time", "look_num", "share_num",
            "like_num");
        if (validSortFields.contains(sortBy)) {
            // 根据字段选择排序方式
            switch (sortBy) {
                case "publisher_time":
                    queryWrapper.orderByDesc(CompetitionMetadata::getPublisherTime);
                    break;
                case "look_num":
                    queryWrapper.orderByDesc(CompetitionMetadata::getLookNum);
                    break;
                case "share_num":
                    queryWrapper.orderByDesc(CompetitionMetadata::getShareNum);
                    break;
                case "like_num":
                    queryWrapper.orderByDesc(CompetitionMetadata::getLikeNum);
                    break;
            }
        }
        List<CompetitionMetadata> competitionPage = competitionMapper.selectList(queryWrapper);
        List<CompetitionResponse> competitionResponses = competitionPage.stream()
            .map(competitionMetadata ->
            {
                CompetitionResponse response = new CompetitionResponse();
                response.setCompetitionId(competitionMetadata.getCompetitionId());
                response.setName(competitionMetadata.getName());
                response.setDescription(competitionMetadata.getDescription());
                return response;
            }).collect(Collectors.toList());

        // Step 5: 手动分页
        Page<CompetitionResponse> page = new Page<>(pageNum, pageSize);
        int total = competitionResponses.size();
        int start = (pageNum - 1) * pageSize;
        int end = Math.min(start + pageSize, total);
        //  防止序列化失败，重新new 一个 数组不能直接使用 subList     
        List<CompetitionResponse> pagedCompetitions = new ArrayList<>(
            competitionResponses.subList(start, end));

        // Step 6: 设置分页对象
        page.setRecords(pagedCompetitions);
        page.setTotal(total);
        page.setPages((int) Math.ceil((double) total / pageSize));

        return page;

    }

    @Override
    //获取赛事详细信息
    public CompetitionResponse getAnnouncementInfoById(Long competitionId, String userId) {

        List<AnnouncementInfo> announcementInfos = announcementInfoMapper.selectByForeignKeyId(
            competitionId);
        CompetitionMetadata competitionMetadata = competitionMapper.selectById(competitionId);

        if (announcementInfos == null) {
            throw new RuntimeException("Announcement not found with id " + competitionId);
        }
        //考虑要不要直接改为CompetitionResponse
        CompetitionResponse dto = new CompetitionResponse();
        dto.setCompetitionId(competitionMetadata.getCompetitionId());
        dto.setText(competitionMetadata.getText());
        dto.setName(competitionMetadata.getName());
        dto.setDescription(competitionMetadata.getDescription());
        dto.setLikeNum(competitionMetadata.getLikeNum());
//        dto.getLookNum(competitionMetadata.getLookNum());
        dto.setLookNum(competitionMetadata.getLookNum());
        dto.setPublisherTime(competitionMetadata.getPublisherTime());
        dto.setShareNum(competitionMetadata.getShareNum());
        dto.setStatus(competitionMetadata.getStatus());

        List<String> projectTags = innovationTagsMapper.getTagsByProjectId(dto.getCompetitionId());
        dto.setTags(projectTags);
        //获取收藏状态
        int count = userLikeMapper.countUserCollect(competitionId, userId);
        if (count > 0) {
            dto.setLikeTrue(true);
        }

        Map<String, String> URLs = new HashMap<>();
//        for(AnnouncementInfo announcementInfo : announcementInfos){
//            boolean filterByFilePath = announcementInfo.getFilePath() != null && !announcementInfo.getFilePath().isEmpty();
//            boolean filterByFileName = announcementInfo.getFileName() != null && !announcementInfo.getFileName().isEmpty();
//            boolean filterByminio = filterByFilePath && filterByFileName;
//            if (filterByminio){
//                //minio迁移至从OSS中获取,这个是获取赛事信息的附件,但现在应该取消掉了
//                URLs.put(announcementInfo.getFileName(),(minioService.getImageUrlWithAnyFormat(announcementInfo.getFilePath() + announcementInfo.getFileName())));
//            }
//        }
//        dto.setFiles(URLs);
        return dto;
    }

    //已经修改为对收藏的判断
    @Transactional
    @Override
    public String toggleLike(Long competitionId, String userId) {
//        int count = userLikeMapper.countUserLike(competitionId, userId);
        int count = userLikeMapper.countUserCollect(competitionId, userId);
        if (count > 0) {
//            userLikeMapper.deleteUserLike(competitionId, userId);
            userLikeMapper.deleteUserCollect(competitionId, userId);
            competitionMapper.decrementLikeNum(competitionId);
            return "取消收藏";
        } else {
//            userLikeMapper.insertUserLike(competitionId, userId);
            userLikeMapper.insertUserCollect(competitionId, userId);//"Competition"
            competitionMapper.incrementLikeNum(competitionId);
            return "成功收藏";
        }
    }

    @Override
    public boolean incrementLookNum(Long competitionId) {
        return competitionMapper.incrementLookNum(competitionId) > 0;
    }

    @Override
    public boolean incrementShareNum(Long competitionId) {
        return competitionMapper.incrementShareNum(competitionId) > 0;
    }

    @Override
    public File downloadFileFromUrl(String fileUrl) throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");

        // 设置文件名为URL中的文件名部分
        String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);

        // 创建临时文件
        File tempFile = File.createTempFile(fileName, ".tmp");
        tempFile.deleteOnExit(); // JVM退出时删除临时文件

        try (InputStream inputStream = connection.getInputStream();
            FileOutputStream outputStream = new FileOutputStream(tempFile)) {

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }

        return tempFile;
    }

    private static final int MAX_RETRIES = 3; // 最大重试次数
    private static final int RETRY_DELAY_MS = 20000; // 每次重试间隔20秒

    //后端代理访问OSS链接
    @Override
    public ResponseEntity<Object> getFileFromLink(String link) {
        int attempts = 0;
        while (attempts < MAX_RETRIES) {
            attempts = attempts + 1;
            try {
                // 设置超时时间
                int connectTimeout = 120000; // 120秒
                int readTimeout = 120000;    // 120秒

                URL url = new URL(link);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");

                // 设置连接超时和读取超时
                connection.setConnectTimeout(connectTimeout);
                connection.setReadTimeout(readTimeout);

                // 检查响应代码
                int responseCode = connection.getResponseCode();
                if (responseCode != HttpURLConnection.HTTP_OK) {
                    return ResponseEntity.status(responseCode).body("无法访问指定链接");
                }

                InputStream inputStream = connection.getInputStream();
                InputStreamResource resource = new InputStreamResource(inputStream);

                // 返回文件内容给前端
                return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=announcement.docx")
                    .body(resource);

            } catch (Exception e) {
                if (attempts >= MAX_RETRIES) {
                    return ResponseEntity.status(500).body("服务器内部错误：" + e.getMessage());
                }
                // 重试前等待一段时间
                try {
                    Thread.sleep(RETRY_DELAY_MS);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    return ResponseEntity.status(500).body("服务器内部错误：" + ie.getMessage());
                }
            }
        }
        return ResponseEntity.status(500).body("服务器无法处理请求");
    }

}
